/**
  ******************************************************************************
  * @file    usbh_adb_core.c
  * @author  Newtons_Prism
  * @version V1.0.0
  * @date    19-April-2022
  * @brief   USB ADB Library
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2022 Newtons_Prism</center></h2>
  *
  * Licensed under GNU General Public License v3.0, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        https://www.gnu.org/licenses/gpl-3.0
  *
  * Unless required by applicable law or agreed to in writing, software 
  * distributed under the License is distributed on an "AS IS" BASIS, 
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */


#include "usbh_adb_class.h"

#include "bsp_led.h"
#include "bsp_timer.h"


__align(4) ADB_Machine_TypeDef ADB_Machine;

/** @defgroup USBH_ADB_CORE_Private_FunctionPrototypes
  * @{
  */ 
static usb_sts_type uhost_init_handler(void *uhost);
static usb_sts_type uhost_reset_handler(void *uhost);
static usb_sts_type uhost_request_handler(void *uhost);
static usb_sts_type uhost_process_handler(void *uhost);

static usb_sts_type usbh_adb_send_auth(void *uhost);
static usb_sts_type usbh_adb_send_cmd(void *uhost);

otg_core_type otg_core_struct;

usbh_class_handler_type uhost_adb_class_handler =  //ADB接口句柄
{
	uhost_init_handler,
  uhost_reset_handler,
  uhost_request_handler,
  uhost_process_handler,
};


/**
  * @brief  USBH_ADB_InterfaceInit 
  *         Interface initialization for ADB class.
  * @param  pdev: Selected device
  * @param  phost: Selected device property
  * @retval USBH_Status : Status of class request handled.
  */
static usb_sts_type uhost_init_handler(void *uhost)
{
	usbh_core_type *puhost = (usbh_core_type *)uhost;
	uint8_t i, j;
	for (i = 0; i < puhost->dev.cfg_desc.cfg.bNumInterfaces; i++)  						//遍历设备接口号
	{
		if((puhost->dev.cfg_desc.interface[i].interface.bInterfaceClass == ADB_CLASS) && \
			(puhost->dev.cfg_desc.interface[i].interface.bInterfaceSubClass == ADB_SUBCLASS)  && \
			(puhost->dev.cfg_desc.interface[i].interface.bInterfaceProtocol == ADB_PROTOCOL))   		//匹配ADB接口
		{
			for (j = 0; j < puhost->dev.cfg_desc.interface[i].interface.bNumEndpoints; j++)   //遍历端点号
			{
				if (puhost->dev.cfg_desc.interface[i].endpoint[j].bEndpointAddress & 0x80)    //匹配ADB输入端点，高位为1是输入
				{
					ADB_Machine.BulkInEp = puhost->dev.cfg_desc.interface[i].endpoint[j].bEndpointAddress;   //获取端点地址
					ADB_Machine.BulkInEpSize  = puhost->dev.cfg_desc.interface[i].endpoint[j].wMaxPacketSize; //获取端点最大数据容量
				}
				else
				{
					ADB_Machine.BulkOutEp = puhost->dev.cfg_desc.interface[i].endpoint[j].bEndpointAddress;  //匹配ADB输出端点
					ADB_Machine.BulkOutEpSize  = puhost->dev.cfg_desc.interface[i].endpoint[j].wMaxPacketSize;
					ADB_Machine.zero_mask = ADB_Machine.BulkOutEpSize - 1;  //零掩码，用于检验数据传输完毕
				}
			}
			ADB_Machine.hc_num_out = usbh_alloc_channel(puhost, ADB_Machine.BulkOutEp);   //用端点地址匹配端点号
			ADB_Machine.hc_num_in = usbh_alloc_channel(puhost, ADB_Machine.BulkInEp);  
			usbh_hc_open(puhost,												//打开ADB输出端点
									 ADB_Machine.hc_num_out,
									 ADB_Machine.BulkOutEp,
									 puhost->dev.address,
									 EPT_BULK_TYPE,								//BULK传输模式
									 ADB_Machine.BulkOutEpSize,
									 puhost->dev.speed);  
			usbh_hc_open(puhost,												//打开ADB输入端点
									 ADB_Machine.hc_num_in,
									 ADB_Machine.BulkInEp,
									 puhost->dev.address,
									 EPT_BULK_TYPE,								//BULK传输模式
									 ADB_Machine.BulkInEpSize,
									 puhost->dev.speed);   
			ADB_Machine.state = ADB_SEND_AUTH;  //进入发送AUTH KEY状态（向设备申请USB调试权限）
			USBH_DEBUG("> USB_ADB_Init\r\n");
			return USB_OK;
		}
	}
	return USB_FAIL;
}


/**
  * @brief  USBH_ADB_InterfaceDeInit 
  *         De-Initialize interface by freeing host channels allocated to interface
  * @param  pdev: Selected device
  * @param  phost: Selected device property
  * @retval None
  */
static usb_sts_type uhost_reset_handler(void *uhost)
{
	usbh_core_type *puhost = (usbh_core_type *)uhost;
	usb_sts_type status = USB_OK;
	if (ADB_Machine.hc_num_out)
  {
		usbh_free_channel(puhost, ADB_Machine.hc_num_out);  //释放ADB输出端点传输
    usbh_ch_disable(puhost, ADB_Machine.hc_num_out);    //停止ADB输出端点传输
    ADB_Machine.hc_num_out = 0;
  }
   
  if (ADB_Machine.hc_num_in)
  {
		usbh_free_channel(puhost, ADB_Machine.hc_num_in);	//释放ADB输出端点传输
    usbh_ch_disable(puhost, ADB_Machine.hc_num_in);		//停止ADB输入端点传输
    ADB_Machine.hc_num_in = 0;
  } 
	USBH_DEBUG("> USB_ADB_Deinit\r\n");
	return status;
}


/**
  * @brief  USBH_ADB_ClassRequest 
  *         This function will only initialize the ADB state machine
  * @param  pdev: Selected device
  * @param  phost: Selected device property
  * @retval USBH_Status : Status of class request handled.
  */
static usb_sts_type uhost_request_handler(void *uhost)
{
	usb_sts_type status = USB_OK;  //ADB类无需进行请求
	USBH_DEBUG("> USB_ADB_ClassRequest\r\n");
	return status;
}


/**
  * @brief  USBH_ADB_Handle 
  *         ADB state machine handler 
  * @param  pdev: Selected device
  * @param  phost: Selected device property
  * @retval USBH_Status
  */
static usb_sts_type uhost_process_handler(void *uhost)
{
	usbh_core_type *puhost = (usbh_core_type *)uhost;
	usb_sts_type status = USB_OK;
	
	switch (ADB_Machine.state)
	{
		case ADB_SEND_AUTH:    //发送AUTH KEY
			USBH_DEBUG("> USB_ADB_SEND_AUTH\r\n");
			if (USB_OK == usbh_adb_send_auth(uhost))
			{
				USBH_DEBUG("> USB_ADB_SEND_AUTH SUCCESS\r\n");
				ADB_Machine.state = ADB_SEND_CMD;   //进入命令行发送状态
			}
			break;
			
		case ADB_SEND_CMD:   //发送SHELL命令行
			USBH_DEBUG("> USB_ADB_SEND_CMD\r\n");
			if (USB_OK == usbh_adb_send_cmd(uhost))
			{
				ADB_Machine.state = ADB_IDLE;   //进入空闲状态
				USBH_DEBUG("> USB_ADB_SEND_CMD_SUCCESS\r\n");
				tmr_counter_enable(BASE_TIM, FALSE);
				LED_ON();
			}
			break;
		
		default:
			break;
	}
	return status;
}


/**
  * @brief  get_apacket 
  *         ADB通信数据包动态分配内存 
  * @param  None
  * @retval 数据包指针
  */
static apacket* get_apacket(void)
{
	apacket* p = (apacket*)malloc(sizeof(apacket));
	memset(p, 0, sizeof(apacket));
	return p;
}

/**
  * @brief  put_apacket 
  *         ADB通信数据包内存释放
	* @param  p: 数据包指针
  * @retval None
  */
static void put_apacket(apacket* p)
{
	free(p);
}

/**
  * @brief  usb_read 
  *         USB读取数据
	* @param  pdev: USB设备句柄
	* @param  data: 数据指针
	* @param  p: 数据长度
	* @retval 成功: 0 失败: -1
  */
static int usb_read(void *uhost, void* data, int len)
{
	usbh_core_type *puhost = (usbh_core_type *)uhost;
	if (puhost != NULL)
	{
		int xfer = (len > MAX_PAYLOAD) ? MAX_PAYLOAD : len;
		do
		{
			usbh_bulk_recv(puhost, ADB_Machine.hc_num_in, (uint8_t*)data, xfer); 			
			usb_delay_ms(50);
		}
		while (!(puhost->urb_state[ADB_Machine.hc_num_in] == URB_DONE));  //阻塞式读取
		return 0;
	}
	return -1;
}

/**
  * @brief  check_header 
  *         检验接收到的数据头
	* @param  p: 数据包指针
	* @retval 成功: 0 失败: -1
  */
static int check_header(apacket* p)
{
	if (p->msg.magic != (p->msg.command ^ 0xffffffff))
	{
		return -1;
	}
	if (p->msg.data_length > MAX_PAYLOAD)
	{
		return -1;
	}
	return 0;
}

/**
  * @brief  check_data 
  *         检验接收到的数据体
	* @param  p: 数据包指针
	* @retval 成功: 0 失败: -1
  */
static int check_data(apacket* p)
{
	uint8_t* x;
	uint16_t count, sum;
	count = p->msg.data_length;
	x = p->data;
	for (sum = 0; count > 0; count--)
	{
		sum += *x++;
	}
	if (sum != p->msg.data_check)
	{
		return -1;
	}
	return 0;
}

/**
  * @brief  remote_read 
  *         读取数据包
	* @param  pdev: USB设备句柄
	* @param  p: 数据包指针
	* @retval 成功: 0 失败: -1
  */
static int remote_read(void *uhost, apacket* p)
{
	usbh_core_type *puhost = (usbh_core_type *)uhost;
	if (usb_read(puhost, &p->msg, sizeof(amessage)))  //读取数据头
	{
		return -1;
	}
	usb_delay_ms(10);
//	printf("remote_read p->cmd:%04x\r\n", p->msg.command);
	if (check_header(p))  //校验数据头
	{
		return -1;
	}
	if (p->msg.data_length)
	{
		if (usb_read(puhost, p->data, p->msg.data_length))  //读取数据体
		{
			return -1;
		}
		usb_delay_ms(10);
//		printf("remote_read p->data:%s\r\n", p->data);
		if (check_data(p))   //校验数据体
		{
			return -1;
		}
	}
	return 0;
}

/**
  * @brief  usb_write 
  *         USB写数据
	* @param  pdev: USB设备句柄
	* @param  data: 数据指针
	* @param  len:  数据长度
	* @retval 成功: 0 失败: -1
  */
static int usb_write(void *uhost, const void* data, int len)
{
	usbh_core_type *puhost = (usbh_core_type *)uhost;
	if (puhost != NULL)
	{
		usbh_bulk_send(puhost, ADB_Machine.hc_num_out, (uint8_t*)data, len);
		if (ADB_Machine.zero_mask && (len & ADB_Machine.zero_mask) == 0)  //检验数据是否发送完毕
		{
			usbh_bulk_send(puhost, ADB_Machine.hc_num_out, (uint8_t*)data, 0);
		}
		return 0;
	}
	return -1;
}


/**
  * @brief  remote_write 
  *         写数据包
	* @param  pdev: USB设备句柄
	* @param  p: 数据包指针
	* @retval 成功: 0 失败: -1
  */
static int remote_write(void *uhost, apacket* p)
{
	usbh_core_type *puhost = (usbh_core_type *)uhost;
	uint16_t len = p->msg.data_length;
	if (usb_write(puhost, &p->msg, sizeof(amessage)))  //写数据头
	{
		return -1;
	}
	usb_delay_ms(2);  //需要延时，区分数据头和数据体
//	printf("remote_write p->cmd:%04x\r\n", p->msg.command);
	if (p->msg.data_length == 0)
	{
		return 0;
	}
	if (usb_write(puhost, p->data, len))   //写数据体
	{
		return -1;
	}
	usb_delay_ms(2);
//	printf("remote_write p->data:%s\r\n", p->data);
	return 0;
}


/**
  * @brief  send_packet_remote 
  *         发送数据包
	* @param  pdev: USB设备句柄
	* @param  p: 数据包指针
	* @retval 成功: 0 失败: -1
  */
static int send_packet_remote(void *uhost, apacket* p)
{
	usbh_core_type *puhost = (usbh_core_type *)uhost;
	uint8_t* x;
	uint16_t count, sum;
	p->msg.magic = p->msg.command ^ 0xffffffff;
	count = p->msg.data_length;
	x = p->data;
	for (sum = 0; count > 0; count--)
	{
		sum += *x++;
	}
	p->msg.data_check = sum;
	if (remote_write(puhost, p))
	{
		return -1;
	}
	return 0;
}


/**
  * @brief  send_auth_remote 
  *         发送AUTH KEY，向设备申请USB调试权限
	* @param  pdev: USB设备句柄
	* @param  key: AUTH KEY（任意字符串）
	* @retval 成功: 0 失败: -1
  */
static int send_auth_remote(void *uhost, const char* key)
{
	usbh_core_type *puhost = (usbh_core_type *)uhost;
	int r;
	uint16_t len;
	apacket* p = get_apacket();
	len = (uint16_t)strlen(key) + 1;
	if (len > (MAX_PAYLOAD - 1))
	{
		printf("destination oversized\r\n");
		put_apacket(p);
		return -1;
	}
	do
	{
		/*打包数据*/
		p->msg.command = A_AUTH;
		p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
		p->msg.arg1 = 0;
		p->msg.data_length = len;
		strcpy((char*)p->data, key);
		r = send_packet_remote(puhost, p);
		do
		{
			memset(p, 0, sizeof(apacket));
			r = remote_read(puhost, p);
		}
		while (!(r == 0));
	}
	while (!(p->msg.command == A_CNXN));  //阻塞式发送，直到接收到A_CNXN才结束
	put_apacket(p);
	return 0;
}


/**
  * @brief  send_cmd_remote 
  *         发送SHELL命令行
	* @param  pdev: USB设备句柄
	* @param  cmd: 命令行字符串
	* @retval 成功: 0 失败: -1
  */
int send_cmd_remote(void *uhost, const char* cmd)
{
	usbh_core_type *puhost = (usbh_core_type *)uhost;
	uint16_t len;
	apacket* p = get_apacket();
	len = (uint16_t)strlen(cmd) + 1;
	if (len > (MAX_PAYLOAD - 1))
	{
		printf("destination oversized\r\n");
		put_apacket(p);
		return -1;
	}
	/*打包数据*/
	p->msg.command = A_OPEN;
	p->msg.arg0 = A_VERSION;
	p->msg.arg1 = 0;
	p->msg.data_length = len;
	strcpy((char*)p->data, cmd);
	send_packet_remote(puhost, p);
	do
	{
		memset(p, 0, sizeof(apacket));
		remote_read(puhost, p);
	}
	while (!(p->msg.command == A_WRTE));  //阻塞式读取，直到读取到A_WRTE才结束
	put_apacket(p);
	return 0;
}

/**
  * @brief  USBH_ADB_SEND_AUTH 
  *         send_auth_remote
	* @param  pdev: USB设备句柄
	* @retval 成功: 0 失败: -1
  */
static usb_sts_type usbh_adb_send_auth(void *uhost)
{
	usbh_core_type *puhost = (usbh_core_type *)uhost;
	if (send_auth_remote(puhost, AUTH_KEY))
	{
		return USB_FAIL;
	}
	return USB_OK;
}

/**
  * @brief  USBH_ADB_SEND_CMD 
  *         send_cmd_remote
	* @param  pdev: USB设备句柄
	* @retval 成功: 0 失败: -1
  */
static usb_sts_type usbh_adb_send_cmd(void *uhost)
{
	usbh_core_type *puhost = (usbh_core_type *)uhost;
	if (send_cmd_remote(puhost, TOUCH_CMD))
	{
		return USB_FAIL;
	}
	return USB_OK;
}


